home *** CD-ROM | disk | FTP | other *** search
/ PC Open 101 / PC Open 101 CD 1.bin / CD1 / INTERNET / EMAIL / pop file / setup.exe / Proxy / POP3.pm < prev    next >
Encoding:
Perl POD Document  |  2004-09-21  |  28.6 KB  |  777 lines

  1. # POPFILE LOADABLE MODULE
  2. package Proxy::POP3;
  3.  
  4. use Proxy::Proxy;
  5. use Digest::MD5;
  6. @ISA = ("Proxy::Proxy");
  7.  
  8. # ---------------------------------------------------------------------------------------------
  9. #
  10. # This module handles proxying the POP3 protocol for POPFile.
  11. #
  12. # Copyright (c) 2001-2004 John Graham-Cumming
  13. #
  14. #   This file is part of POPFile
  15. #
  16. #   POPFile is free software; you can redistribute it and/or modify
  17. #   it under the terms of the GNU General Public License as published by
  18. #   the Free Software Foundation; either version 2 of the License, or
  19. #   (at your option) any later version.
  20. #
  21. #   POPFile is distributed in the hope that it will be useful,
  22. #   but WITHOUT ANY WARRANTY; without even the implied warranty of
  23. #   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  24. #   GNU General Public License for more details.
  25. #
  26. #   You should have received a copy of the GNU General Public License
  27. #   along with POPFile; if not, write to the Free Software
  28. #   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  29. #
  30. #   Modified by     Sam Schinke (sschinke@users.sourceforge.net)
  31. #
  32. # ---------------------------------------------------------------------------------------------
  33.  
  34. use strict;
  35. use warnings;
  36. use locale;
  37.  
  38. # A handy variable containing the value of an EOL for networks
  39. my $eol = "\015\012";
  40.  
  41. #----------------------------------------------------------------------------
  42. # new
  43. #
  44. #   Class new() function
  45. #----------------------------------------------------------------------------
  46. sub new
  47. {
  48.     my $type = shift;
  49.     my $self = Proxy::Proxy->new();
  50.  
  51.     # Must call bless before attempting to call any methods
  52.  
  53.     bless $self, $type;
  54.  
  55.     $self->name( 'pop3' );
  56.  
  57.     $self->{child_} = \&child__;
  58.     $self->{connection_timeout_error_} = '-ERR no response from mail server';
  59.     $self->{connection_failed_error_}  = '-ERR can\'t connect to';
  60.     $self->{good_response_}            = '^\+OK';
  61.  
  62.     # Client requested APOP
  63.     $self->{use_apop__} = 0;
  64.  
  65.     # APOP username
  66.     $self->{apop_user__} = '';
  67.  
  68.     # The APOP portion of the banner sent by the POP3 server
  69.     $self->{apop_banner__} = undef;
  70.  
  71.     return $self;
  72. }
  73.  
  74. # ---------------------------------------------------------------------------------------------
  75. #
  76. # initialize
  77. #
  78. # Called to initialize the POP3 proxy module
  79. #
  80. # ---------------------------------------------------------------------------------------------
  81. sub initialize
  82. {
  83.     my ( $self ) = @_;
  84.  
  85.     # Enabled by default
  86.     $self->config_( 'enabled', 1);
  87.  
  88.     # By default we don't fork on Windows
  89.     $self->config_( 'force_fork', ($^O eq 'MSWin32')?0:1 );
  90.  
  91.     # Default ports for POP3 service and the user interface
  92.     $self->config_( 'port', 110 );
  93.  
  94.     # There is no default setting for the secure server
  95.     $self->config_( 'secure_server', '' );
  96.     $self->config_( 'secure_port', 110 );
  97.  
  98.     # Only accept connections from the local machine for POP3
  99.     $self->config_( 'local', 1 );
  100.  
  101.     # Whether to do classification on TOP as well
  102.     $self->config_( 'toptoo', 0 );
  103.  
  104.     # The separator within the POP3 username is :
  105.     $self->config_( 'separator', ':' );
  106.  
  107.     # The welcome string from the proxy is configurable
  108.     $self->config_( 'welcome_string',
  109.         "POP3 POPFile ($self->{version_}) server ready" );
  110.  
  111.     return $self->SUPER::initialize();
  112. }
  113.  
  114. # ---------------------------------------------------------------------------------------------
  115. #
  116. # start
  117. #
  118. # ---------------------------------------------------------------------------------------------
  119. sub start
  120. {
  121.     my ( $self ) = @_;
  122.  
  123.     # If we are not enabled then no further work happens in this module
  124.  
  125.     if ( $self->config_( 'enabled' ) == 0 ) {
  126.         return 2;
  127.     }
  128.  
  129.     # Tell the user interface module that we having a configuration
  130.     # item that needs a UI component
  131.  
  132.     $self->register_configuration_item_( 'configuration',              # PROFILE BLOCK START
  133.                                          'pop3_configuration',
  134.                                          'pop3-configuration-panel.thtml',
  135.                                          $self );                      # PROFILE BLOCK STOP
  136.  
  137.     $self->register_configuration_item_( 'security',                   # PROFILE BLOCK START
  138.                                          'pop3_security',
  139.                                          'pop3-security-panel.thtml',
  140.                                          $self );                      # PROFILE BLOCK STOP
  141.  
  142.     $self->register_configuration_item_( 'chain',                      # PROFILE BLOCK START
  143.                                          'pop3_chain',
  144.                                          'pop3-chain-panel.thtml',
  145.                                          $self );                      # PROFILE BLOCK STOP
  146.  
  147.     if ( $self->config_( 'welcome_string' ) =~ /^POP3 POPFile \(v\d+\.\d+\.\d+\) server ready$/ ) { # PROFILE BLOCK START
  148.         $self->config_( 'welcome_string', "POP3 POPFile ($self->{version_}) server ready" );        # PROFILE BLOCK STOP
  149.     }
  150.  
  151.     return $self->SUPER::start();
  152. }
  153.  
  154. # ---------------------------------------------------------------------------------------------
  155. #
  156. # child__
  157. #
  158. # The worker method that is called when we get a good connection from a client
  159. #
  160. # $client         - an open stream to a POP3 client
  161. # $session        - API session key
  162. #
  163. # ---------------------------------------------------------------------------------------------
  164. sub child__
  165. {
  166.     my ( $self, $client, $session ) = @_;
  167.  
  168.     # Hash of indexes of downloaded messages mapped to their
  169.     # slot IDs
  170.  
  171.     my %downloaded;
  172.  
  173.     # The handle to the real mail server gets stored here
  174.  
  175.     my $mail;
  176.  
  177.     $self->{apop_banner__} = undef;
  178.     $self->{use_apop__} = 0;
  179.     $self->{apop_user__} = '';
  180.  
  181.     # Tell the client that we are ready for commands and identify our
  182.     # version number
  183.  
  184.     $self->tee_( $client, "+OK " . $self->config_( 'welcome_string' ) . "$eol" );
  185.  
  186.     # Compile some configurable regexp's once
  187.  
  188.     my $s = $self->config_( 'separator' );
  189.     $s =~ s/(\$|\@|\[|\]|\(|\)|\||\?|\*|\.|\^|\+)/\\$1/;
  190.  
  191.     my $transparent  = "^USER ([^$s])+\$";
  192.     my $user_command = "USER ([^$s]+)($s(\\d+))?$s([^$s]+)($s([^$s]+))?";
  193.     my $apop_command = "APOP ([^$s]+)($s(\\d+))?$s([^$s]+) (.*?)";
  194.  
  195.     $self->log_( 2, "Regexps: $transparent, $user_command, $apop_command" );
  196.  
  197.     # Retrieve commands from the client and process them until the
  198.     # client disconnects or we get a specific QUIT command
  199.  
  200.     while  ( <$client> ) {
  201.         my $command;
  202.  
  203.         $command = $_;
  204.  
  205.         # Clean up the command so that it has a nice clean $eol at the
  206.         # end
  207.  
  208.         $command =~ s/(\015|\012)//g;
  209.  
  210.         $self->log_( 2, "Command: --$command--" );
  211.  
  212.         # The USER command is a special case because we modify the
  213.         # syntax of POP3 a little to expect that the username being
  214.         # passed is actually of the form host:username where host is
  215.         # the actual remote mail server to contact and username is the
  216.         # username to pass through to that server and represents the
  217.         # account on the remote machine that we will pull email from.
  218.         # Doing this means we can act as a proxy for multiple mail
  219.         # clients and mail accounts
  220.         #
  221.         # When the client issues the command "USER host:username:apop"
  222.         # POPFile must acknowledge the command and be prepared to
  223.         # compute the md5 digest of the user's password and the real
  224.         # pop server's banner upon receipt of a PASS command.
  225.         #
  226.         # When the client issues the command "USER host:username:ssl"
  227.         # POPFile will use SSL for the connection to the remote, note
  228.         # that the user can say host:username:ssl,apop if both are
  229.         # needed
  230.  
  231.         if ( $command =~ /$transparent/ ) {
  232.             if ( $self->config_( 'secure_server' ) ne '' )  {
  233.                 if ( $mail = $self->verify_connected_( $mail, $client,  $self->config_( 'secure_server' ), $self->config_( 'secure_port' ) ) )  {
  234.                     last if ($self->echo_response_($mail, $client, $command) == 2 );
  235.                 } else {
  236.                     next;
  237.                 }
  238.             } else {
  239.                 $self->tee_(  $client, "-ERR Transparent proxying not configured: set secure server/port$eol" );
  240.             }
  241.  
  242.             next;
  243.         }
  244.  
  245.         if ( $command =~ /$user_command/i ) {
  246.             if ( $1 ne '' )  {
  247.                 my ( $host, $port, $user, $options ) = ($1, $3, $4, $6);
  248.  
  249.                 $self->mq_post_( 'LOGIN', $user );
  250.  
  251.                 my $ssl = defined( $options ) && ( $options =~ /ssl/i );
  252.                 $port = 110 if ( !defined( $port ) );
  253.  
  254.                 if ( $mail = $self->verify_connected_( $mail, $client, $host, $port, $ssl ) )  {
  255.  
  256.                     if ( defined( $options ) && ( $options =~ /apop/i ) ) {
  257.  
  258.                         # We want to make sure the server sent a real
  259.                         # APOP banner, containing <>'s
  260.  
  261.                         $self->{apop_banner__} = $1 if $self->{connect_banner__} =~ /(<[^>]+>)/;
  262.                         $self->log_( 2, "banner=" . $self->{apop_banner__} ) if defined( $self->{apop_banner__} );
  263.  
  264.                         # any apop banner is ok
  265.  
  266.                         if ( defined($self->{apop_banner__})) {
  267.                             $self->{use_apop__} = 1; #
  268.                             $self->log_( 2, "auth APOP" );
  269.                             $self->{apop_user__} = $user;
  270.  
  271.                             # tell the client that username was
  272.                             # accepted don't flush_extra, we didn't
  273.                             # send anything to the real server
  274.  
  275.                             $self->tee_( $client, "+OK hello $user$eol" );
  276.                             next;
  277.                         } else {
  278.  
  279.                             # If the client asked for APOP, and the
  280.                             # server doesn't have the correct banner,
  281.                             # give a meaningful error instead of
  282.                             # whatever error the server might have if
  283.                             # we try to make up a hash
  284.  
  285.                             $self->{use_apop__} = 0;
  286.                             $self->tee_( $client, "-ERR $host doesn't support APOP, aborting authentication$eol" );
  287.                             next;
  288.                         }
  289.                     } else {
  290.  
  291.                         # Pass through the USER command with the
  292.                         # actual user name for this server, and send
  293.                         # the reply straight to the client
  294.  
  295.                         $self->log_( 2, "auth plaintext" );
  296.                         $self->{use_apop__} = 0;         # signifies a non-apop connection
  297.                         last if ($self->echo_response_( $mail, $client, 'USER ' . $user ) == 2 );
  298.                     }
  299.  
  300.                 } else {
  301.  
  302.                     # If the login fails then we want to continue in
  303.                     # the unlogged in state so that clients can send
  304.                     # us the QUIT command
  305.  
  306.                     next;
  307.                 }
  308.             }
  309.  
  310.             next;
  311.         }
  312.  
  313.         # User is issuing the APOP command to start a session with the
  314.         # remote server
  315.  
  316.         if ( ( $command =~ /PASS (.*)/i ) ) {
  317.             if ( $self->{use_apop__} ) {
  318.  
  319.                 # Authenticate with APOP
  320.  
  321.                 my $md5 = Digest::MD5->new;
  322.  
  323.                 $md5->add( $self->{apop_banner__}, $1 );
  324.                 my $md5hex = $md5->hexdigest;
  325.                 $self->log_( 2, "digest='$md5hex'" );
  326.  
  327.                 my ($response, $ok) =
  328.                     $self->get_response_( $mail, $client,
  329.                         "APOP $self->{apop_user__} $md5hex", 0, 1 );
  330.                 if ( ( $ok == 1 ) &&
  331.                      ( $response =~ /$self->{good_response_}/ ) ) {
  332.  
  333.                     # authentication OK, toss the hello response and
  334.                     # return password ok
  335.  
  336.                     $self->tee_( $client, "+OK password ok$eol" );
  337.                 } else {
  338.                     $self->tee_( $client, "$response" );
  339.                 }
  340.              } else {
  341.                last if ($self->echo_response_($mail, $client, $command) == 2 );
  342.              }
  343.              next;
  344.         }
  345.  
  346.         # User is issuing the APOP command to start a session with the
  347.         # remote server We'd need a copy of the plaintext password to
  348.         # support this.
  349.  
  350.         if ( $command =~ /$apop_command/io ) {
  351.             $self->tee_( $client,
  352.               "-ERR APOP not supported between mail client and POPFile.$eol" );
  353.  
  354.             # TODO: Consider implementing a host:port:username:secret
  355.             # hash syntax for proxying the APOP command
  356.  
  357.             next;
  358.         }
  359.  
  360.         # Secure authentication
  361.  
  362.         if ( $command =~ /AUTH ([^ ]+)/ ) {
  363.             if ( $self->config_( 'secure_server' ) ne '' )  {
  364.                 if ( $mail = $self->verify_connected_( $mail, $client,  $self->config_( 'secure_server' ), $self->config_( 'secure_port' ) ) )  {
  365.  
  366.                     # Loop until we get -ERR or +OK
  367.  
  368.                     my ( $response, $ok ) = $self->get_response_( $mail, $client, $command );
  369.  
  370.                     while ( ( ! ( $response =~ /\+OK/ ) ) && ( ! ( $response =~ /-ERR/ ) ) ) {
  371.                         my $auth;
  372.                         $auth = <$client>;
  373.                         $auth =~ s/(\015|\012)$//g;
  374.                         ( $response, $ok ) = $self->get_response_( $mail, $client, $auth );
  375.                     }
  376.                 } else {
  377.                     next;
  378.                 }
  379.             } else {
  380.                 $self->tee_(  $client, "-ERR No secure server specified$eol" );
  381.             }
  382.  
  383.             next;
  384.         }
  385.  
  386.         if ( $command =~ /AUTH/ ) {
  387.             if ( $self->config_( 'secure_server' ) ne '' )  {
  388.                 if ( $mail = $self->verify_connected_( $mail, $client,  $self->config_( 'secure_server' ), $self->config_( 'secure_port' ) ) )  {
  389.                     my $response = $self->echo_response_($mail, $client, "AUTH" );
  390.                     last if ( $response == 2 );
  391.                     if ( $response == 0 ) {
  392.                         $self->echo_to_dot_( $mail, $client );
  393.                     }
  394.                 } else {
  395.                     next;
  396.                 }
  397.             } else {
  398.                 $self->tee_(  $client, "-ERR No secure server specified$eol" );
  399.             }
  400.  
  401.             next;
  402.         }
  403.  
  404.         # The client is requesting a LIST/UIDL of the messages
  405.  
  406.         if ( ( $command =~ /LIST ?(.*)?/i ) ||       # PROFILE BLOCK START
  407.              ( $command =~ /UIDL ?(.*)?/i ) ) {      # PROFILE BLOCK STOP
  408.             my $response = $self->echo_response_($mail, $client, $command );
  409.             last if ( $response == 2 );
  410.             if ( $response == 0 ) {
  411.                 $self->echo_to_dot_( $mail, $client ) if ( $1 eq '' );
  412.             }
  413.  
  414.             next;
  415.         }
  416.  
  417.         # TOP handling is rather special because we have three cases
  418.         # that we handle
  419.         #
  420.         # 1. If the client sends TOP x 99999999 then it is most likely
  421.         #    to be fetchmail and the intent of fetchmail is to
  422.         #    actually get the message but for its own reasons it does
  423.         #    not use RETR.  We use RETR as the clue to place a message
  424.         #    in the history, so we have a hack.  If the client looks
  425.         #    like fetchmail then TOP x 99999999 is actually
  426.         #    implemented using RETR
  427.         #
  428.         # 2. The toptoo configuration controls whether email
  429.         #    downloaded using the TOP command is classified or not (It
  430.         #    may be downloaded and cached for bandwidth efficiency, and
  431.         #    thus appear in the history).  There are two cases:
  432.         #
  433.         # 2a If toptoo is 0 then POPFile will pass a TOP from the
  434.         #    client through as a TOP and do no classification on the
  435.         #    message.
  436.         #
  437.         # 2b If toptoo is 1 then POPFile first does a RETR on the
  438.         #    message and saves it in the history so that it can get the
  439.         #    classification on the message which is stores in $class.
  440.         #    Then it gets the message again by sending the TOP command
  441.         #    and passing the result through classify_and_modify passing
  442.         #    in the $class determined above.  This means that the message
  443.         #    gets the right classification and the client only gets the
  444.         #    headers requested plus so many lines of body, but they will
  445.         #    get subject modification, and the XTC and XPL headers add.
  446.         #    Note that TOP always returns the full headers and then n
  447.         #    lines of the body so we are guaranteed to be able to do our
  448.         #    header modifications.
  449.         #
  450.         # NOTE messages retrieved using TOPTOO are visible in the
  451.         #      history as they are "cached" to avoid requiring repeated
  452.         #      downloads if the client issues a RETR for the message in
  453.         #      the same session
  454.         #
  455.         # NOTE using toptoo=1 on a slow link could cause
  456.         #      performance problems, in cases where only the headers,
  457.         #      but not classification, is required.  toptoo=1 is,
  458.         #      however, appropriate for normal use via a mail client and
  459.         #      won't significantly increase bandwidth unless the mail
  460.         #      client is selectively downloading messages based on
  461.         #      non-classification data in the TOP headers.
  462.  
  463.         if ( $command =~ /TOP (.*) (.*)/i ) {
  464.             my $count = $1;
  465.  
  466.             if ( $2 ne '99999999' )  {
  467.                 if ( $self->config_( 'toptoo' ) == 1 ) {
  468.                     my $response =
  469.                         $self->echo_response_( $mail, $client, "RETR $count" );
  470.                     last if ( $response == 2 );
  471.                     if ( $response == 0 ) {
  472.  
  473.                         # Classify without echoing to client, saving
  474.                         # file for later RETR's
  475.  
  476.                         my ( $class, $slot ) =
  477.                              $self->{classifier__}->classify_and_modify(
  478.                                  $session, $mail, $client, 0, '', 0, 0 );
  479.  
  480.                         $downloaded{$count} = $slot;
  481.  
  482.                         # Note that the 1 here indicates that
  483.                         # echo_response_ does not send the response to
  484.                         # the client.  The +OK has already been sent
  485.                         # by the RETR
  486.  
  487.                         $response =
  488.                             $self->echo_response_( $mail, $client,
  489.                                 $command, 1 );
  490.                         last if ( $response == 2 );
  491.                         if ( $response == 0 ) {
  492.  
  493.                             # Classify with pre-defined class, without
  494.                             # saving, echoing to client
  495.  
  496.                             $self->{classifier__}->classify_and_modify(
  497.                                 $session, $mail, $client, 1, $class, $slot, 1 );
  498.                         }
  499.                     }
  500.                 } else {
  501.                     my $response =
  502.                         $self->echo_response_( $mail, $client, $command );
  503.                     last if ( $response == 2 );
  504.                     if ( $response == 0 ) {
  505.                         $self->echo_to_dot_( $mail, $client );
  506.             }
  507.                 }
  508.  
  509.                 next;
  510.             }
  511.  
  512.             # Note the fall through here.  Later down the page we look
  513.             # for TOP x 99999999 and do a RETR instead
  514.         }
  515.  
  516.         # The CAPA command
  517.  
  518.         if ( $command =~ /CAPA/i ) {
  519.             if ( $mail || $self->config_( 'secure_server' ) ne '' )  {
  520.                 if ( $mail || ( $mail = $self->verify_connected_( $mail, $client, $self->config_( 'secure_server' ), $self->config_( 'secure_port' ) ) ) )  {
  521.                     my $response = $self->echo_response_($mail, $client, "CAPA" );
  522.                     last if ( $response == 2 );
  523.                     if ( $response == 0 ) {
  524.                         $self->echo_to_dot_( $mail, $client );
  525.             }
  526.                 } else {
  527.                     next;
  528.                 }
  529.             } else {
  530.                 $self->tee_(  $client, "-ERR No secure server specified$eol" );
  531.             }
  532.  
  533.             next;
  534.         }
  535.  
  536.         # The HELO command results in a very simple response from us.
  537.         # We just echo that we are ready for commands
  538.  
  539.         if ( $command =~ /HELO/i ) {
  540.             $self->tee_(  $client, "+OK HELO POPFile Server Ready$eol" );
  541.             next;
  542.         }
  543.  
  544.         # In the case of PASS, NOOP, XSENDER, STAT, DELE and RSET
  545.         # commands we simply pass it through to the real mail server
  546.         # for processing and echo the response back to the client
  547.  
  548.         if ( ( $command =~ /NOOP/i )         ||                 # PROFILE BLOCK START
  549.              ( $command =~ /STAT/i )         ||
  550.              ( $command =~ /XSENDER (.*)/i ) ||
  551.              ( $command =~ /DELE (.*)/i )    ||
  552.              ( $command =~ /RSET/i ) ) {                        # PROFILE BLOCK STOP
  553.             last if ( $self->echo_response_($mail, $client, $command ) == 2 );
  554.             next;
  555.         }
  556.  
  557.         # The client is requesting a specific message.  Note the
  558.         # horrible hack here where we detect a command of the form TOP
  559.         # x 99999999 this is done so that fetchmail can be used with
  560.         # POPFile.
  561.  
  562.         if ( ( $command =~ /RETR (.*)/i ) || ( $command =~ /TOP (.*) 99999999/i ) )  {
  563.             my $count = $1;
  564.             my $class;
  565.             my $file;
  566.  
  567.             if ( defined($downloaded{$count}) &&
  568.                  ( $file = $self->{history__}->get_slot_file( $downloaded{$count} ) ) &&
  569.                  (open RETRFILE, "<$file") ) {
  570.  
  571.                 # act like a network stream
  572.  
  573.                 binmode RETRFILE;
  574.  
  575.                 # File has been fetched and classified already
  576.  
  577.                 $self->log_( 1, "Printing message from cache" );
  578.  
  579.                 # Give the client an +OK:
  580.  
  581.                 $self->tee_( $client, "+OK " . ( -s $file ) . " bytes from POPFile cache$eol" );
  582.  
  583.                 # Load the last classification
  584.  
  585.                 my ( $id, $from, $to, $cc, $subject,
  586.                     $date, $hash, $inserted, $bucket, $reclassified ) =
  587.                     $self->{history__}->get_slot_fields( $downloaded{$count} );
  588.  
  589.                 if ( $bucket ne 'unknown class' ) {
  590.  
  591.                     # echo file, inserting known classification,
  592.                     # without saving
  593.  
  594.                     ($class, undef) = $self->{classifier__}->classify_and_modify( $session, \*RETRFILE, $client, 1, $bucket, $downloaded{$count} );
  595.                     print $client ".$eol";
  596.  
  597.                 } else {
  598.  
  599.                     # If the class wasn't saved properly, classify
  600.                     # from disk normally
  601.  
  602.                     ($class, undef) = $self->{classifier__}->classify_and_modify( $session, \*RETRFILE, $client, 1, '', 0 );
  603.                     print $client ".$eol";
  604.                 }
  605.  
  606.                 close RETRFILE;
  607.             } else {
  608.  
  609.                 # Retrieve file directly from the server
  610.  
  611.                 # Get the message from the remote server, if there's
  612.                 # an error then we're done, but if not then we echo
  613.                 # each line of the message until we hit the . at the
  614.                 # end
  615.  
  616.                 my $response = $self->echo_response_($mail, $client, $command );
  617.  
  618.                 last if ( $response == 2 );
  619.                 if ( $response == 0 ) {
  620.                     my $slot;
  621.                     ( $class, $slot ) = $self->{classifier__}->classify_and_modify( $session, $mail, $client, 0, '', 0 );
  622.  
  623.                     # Note locally that file has been retrieved if the
  624.                     # full thing has been saved to disk
  625.  
  626.                     $downloaded{$count} = $slot;
  627.                 }
  628.             }
  629.  
  630.             next;
  631.         }
  632.  
  633.         # The mail client wants to stop using the server, so send that
  634.         # message through to the real mail server, echo the response
  635.         # back up to the client and exit the while.  We will close the
  636.         # connection immediately
  637.  
  638.         if ( $command =~ /QUIT/i ) {
  639.             if ( $mail )  {
  640.                 last if ( $self->echo_response_( $mail, $client, $command ) == 2 );
  641.                 close $mail;
  642.             } else {
  643.                 $self->tee_( $client, "+OK goodbye$eol" );
  644.             }
  645.             last;
  646.         }
  647.  
  648.         # Don't know what this is so let's just pass it through and
  649.         # hope for the best
  650.  
  651.         if ( $mail && $mail->connected )  {
  652.             last if ( $self->echo_response_($mail, $client, $command ) == 2 );
  653.             next;
  654.         } else {
  655.             $self->tee_(  $client, "-ERR unknown command or bad syntax$eol" );
  656.             next;
  657.         }
  658.  
  659.     }
  660.  
  661.     if ( defined( $mail ) ) {
  662.         $self->done_slurp_( $mail );
  663.         close $mail;
  664.     }
  665.  
  666.     close $client;
  667.     $self->mq_post_( 'CMPLT', $$ );
  668.     $self->log_( 0, "POP3 proxy done" );
  669. }
  670.  
  671. # ---------------------------------------------------------------------------------------------
  672. #
  673. # configure_item
  674. #
  675. #    $name            Name of this item
  676. #    $templ           The loaded template that was passed as a parameter
  677. #                     when registering
  678. #    $language        Current language
  679. #
  680. # ---------------------------------------------------------------------------------------------
  681.  
  682. sub configure_item
  683. {
  684.     my ( $self, $name, $templ, $language ) = @_;
  685.  
  686.     if ( $name eq 'pop3_configuration' ) {
  687.         $templ->param( 'POP3_Configuration_If_Force_Fork' => ( $self->config_( 'force_fork' ) == 0 ) );
  688.         $templ->param( 'POP3_Configuration_Port'          => $self->config_( 'port' ) );
  689.         $templ->param( 'POP3_Configuration_Separator'     => $self->config_( 'separator' ) );
  690.     } else {
  691.         if ( $name eq 'pop3_security' ) {
  692.             $templ->param( 'POP3_Security_Local' => ( $self->config_( 'local' ) == 1 ) );
  693.     } else {
  694.             if ( $name eq 'pop3_chain' ) {
  695.                 $templ->param( 'POP3_Chain_Secure_Server' => $self->config_( 'secure_server' ) );
  696.                 $templ->param( 'POP3_Chain_Secure_Port' => $self->config_( 'secure_port' ) );
  697.         } else {
  698.                 $self->SUPER::configure_item( $name, $templ, $language );
  699.         }
  700.         }
  701.     }
  702. }
  703.  
  704. # ---------------------------------------------------------------------------------------------
  705. #
  706. # validate_item
  707. #
  708. #    $name            The name of the item being configured, was passed in by the call
  709. #                     to register_configuration_item
  710. #    $templ           The loaded template
  711. #    $language        The language currently in use
  712. #    $form            Hash containing all form items
  713. #
  714. # ---------------------------------------------------------------------------------------------
  715.  
  716. sub validate_item
  717. {
  718.     my ( $self, $name, $templ, $language, $form ) = @_;
  719.  
  720.     if ( $name eq 'pop3_configuration' ) {
  721.         if ( defined($$form{pop3_port}) ) {
  722.             if ( ( $$form{pop3_port} >= 1 ) && ( $$form{pop3_port} < 65536 ) ) {
  723.                 $self->config_( 'port', $$form{pop3_port} );
  724.                 $templ->param( 'POP3_Configuration_If_Port_Updated' => 1 );
  725.                 $templ->param( 'POP3_Configuration_Port_Updated' => sprintf( $$language{Configuration_POP3Update}, $self->config_( 'port' ) ) );
  726.             } else {
  727.                 $templ->param( 'POP3_Configuration_If_Port_Error' => 1 );
  728.             }
  729.         }
  730.  
  731.         if ( defined($$form{pop3_separator}) ) {
  732.             if ( length($$form{pop3_separator}) == 1 ) {
  733.                 $self->config_( 'separator', $$form{pop3_separator} );
  734.                 $templ->param( 'POP3_Configuration_If_Sep_Updated' => 1 );
  735.                 $templ->param( 'POP3_Configuration_Sep_Updated' => sprintf( $$language{Configuration_POP3SepUpdate}, $self->config_( 'separator' ) ) );
  736.             } else {
  737.                 $templ->param( 'POP3_Configuration_If_Sep_Error' => 1 );
  738.             }
  739.         }
  740.  
  741.         if ( defined($$form{pop3_force_fork}) ) {
  742.             $self->config_( 'force_fork', $$form{pop3_force_fork} );
  743.         }
  744.  
  745.         return;
  746.     }
  747.  
  748.     if ( $name eq 'pop3_security' ) {
  749.         $self->config_( 'local', $$form{pop3_local}-1 ) if ( defined($$form{pop3_local}) );
  750.  
  751.         return;
  752.     }
  753.  
  754.     if ( $name eq 'pop3_chain' ) {
  755.         if ( defined( $$form{server} ) ) {
  756.             $self->config_( 'secure_server', $$form{server} );
  757.             $templ->param( 'POP3_Chain_If_Server_Updated' => 1 );
  758.             $templ->param( 'POP3_Chain_Server_Updated' => sprintf( $$language{Security_SecureServerUpdate}, $self->config_( 'secure_server' ) ) );
  759.     }
  760.  
  761.         if ( defined($$form{sport}) ) {
  762.             if ( ( $$form{sport} >= 1 ) && ( $$form{sport} < 65536 ) ) {
  763.                 $self->config_( 'secure_port', $$form{sport} );
  764.                 $templ->param( 'POP3_Chain_If_Port_Updated' => 1 );
  765.                 $templ->param( 'POP3_Chain_Port_Updated' => sprintf( $$language{Security_SecurePortUpdate}, $self->config_( 'secure_port' ) ) );
  766.             } else {
  767.                 $templ->param( 'POP3_Chain_If_Port_Error' => 1 );
  768.             }
  769.         }
  770.  
  771.         return;
  772.     }
  773.  
  774.     $self->SUPER::validate_item( $name, $templ, $language, $form );
  775. }
  776.  
  777.